#include "header.hpp"
using namespace std;

//Subject: Design Pattern
//Observer pattern or Publish/Subscribe pattern

class Observer;

//Abstract notifier
class Subject{
public:
    virtual void attach(Observer* observer) = 0;
    virtual void detach(Observer* observer) = 0;
    virtual void notify() = 0;
    virtual ~Subject(){}
};


class ConcreteSubject : public Subject{
private:
    list<Observer*> _obList;
    int _status;

public:
    virtual void attach(Observer* pObserver) override{
        if(pObserver){
            _obList.push_back(pObserver);
        }
    }

    virtual void detach(Observer* pObserver) override{
        if(pObserver){
            _obList.remove(pObserver);
        }
    }

    virtual void notify() override;


    void setStatus(int status){
        _status = status;
    }

    int getStatus() const{
        return _status;
    }
};


//Abstract observer
class Observer{
public:
    virtual void update(int) = 0;
    virtual ~Observer(){}
};

class ConcreteObserver : public Observer{
private:
    string _name;

public:
    ConcreteObserver(const string& name)
    : _name(name){}

    void update(int value){
        cout << "ConcreteObserver " << _name << ", value = " << value << endl;
    }
};


class ConcreteObserver2 : public Observer{
private:
    string _name;

public:
    ConcreteObserver2(const string& name)
    : _name(name){}

    void update(int value){
        cout << "ConcreteObserver2 " << _name << ", value = " << value << endl;
    }
};

void ConcreteSubject::notify(){
    for(auto& ob : _obList){
        ob->update(_status);
    }
}


void test14(){
    unique_ptr<ConcreteSubject> pSubject(new ConcreteSubject());
    unique_ptr<Observer> pObserver(new ConcreteObserver("abc"));
    unique_ptr<Observer> pObserver2(new ConcreteObserver2("def"));

    pSubject->setStatus(2);

    pSubject->attach(pObserver.get());
    pSubject->attach(pObserver2.get());

    pSubject->notify();

    pSubject->detach(pObserver2.get());
    pSubject->setStatus(3);
    pSubject->notify();
}


